声明

本篇文章转载自JAVA反射与注解,作者先讲解了Java反射,然后在Java反射的基础上又对Java注解进行了讲解,我主要是对其中Java反射部分进行摘录。

什么是Java反射

主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。

什么是反射机制

反射机制就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

用一句话总结就是反射可以实现在运行时可以知道任意一个类属性和方法

反射机制能做什么

反射机制主要提供了以下功能:

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法;
  • 在运行时调用任意一个对象的方法;
  • 生成动态代理

Java反射机制的应用场景是什么

  • 逆向代码 ,例如反编译
  • 与注解相结合的框架 例如Retrofit
  • 单纯的反射机制应用框架 例如EventBus
  • 动态生成类框架 例如Gson

理解Class类和类类型

想要了解反射首先理解一下Class类,它是反射实现的基础。
类是java.lang.Class类的实例对象,而Class是所有类的类(There is a class named Class)
对于普通的对象,我们一般都会这样创建和表示:

1
Code code1 = new Code();

上面说了,所有的类都是Class的对象,那么如何表示呢,可不可以通过如下方式呢:

1
Class c = new Class();

但是我们查看Class的源码时,是这样写的:

1
2
3
private Class(ClassLoader loader) {
classLoader = loader;
}

可以看到构造器是私有的,只有JVM可以创建Class的对象,因此不可以像普通类一样new一个Class对象,虽然我们不能new一个Class对象,但是却可以通过已有的类得到一个Class对象,共有三种方式,如下:

1
2
3
Class c1 = Code.class; // 这说明任何一个类都有一个隐含的静态成员变量class,这种方式是通过获取类的静态成员变量class得到的
Class c2 = code1.getClass(); // code1是Code的一个对象,这种方式是通过一个类的对象的getClass()方法获得的
Class c3 = Class.forName("com.trigl.reflect.Code"); // 这种方法是Class类调用forName方法,通过一个类的全量限定名获得

这里,c1、c2、c3都是Class的对象,他们是完全一样的,而且有个学名,叫做Code的类类型(class type)。
这里就让人奇怪了,前面不是说Code是Class的对象吗,而c1、c2、c3也是Class的对象,那么Code和c1、c2、c3不就一样了吗?为什么还叫Code什么类类型?这里不要纠结于它们是否相同,只要理解类类型是干什么的就好了,顾名思义,类类型就是类的类型,也就是描述一个类是什么,都有哪些东西,所以我们可以通过类类型知道一个类的属性和方法,并且可以调用一个类的属性和方法,这就是反射的基础。

举个简单例子代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
//第一种:Class c1 = Code.class;
Class class1=ReflectDemo.class;
System.out.println(class1.getName());
//第二种:Class c2 = code1.getClass();
ReflectDemo demo2= new ReflectDemo();
Class c2 = demo2.getClass();
System.out.println(c2.getName());
//第三种:Class c3 = Class.forName("com.trigl.reflect.Code");
Class class3 = Class.forName("com.tengj.reflect.ReflectDemo");
System.out.println(class3.getName());
}
}

执行结果:

1
2
3
com.tengj.reflect.ReflectDemo
com.tengj.reflect.ReflectDemo
com.tengj.reflect.ReflectDemo

Java反射相关操作

在这里先看一下sun为我们提供了那些反射机制中的类:

  • java.lang.Class;
  • java.lang.reflect.Constructor;
  • java.lang.reflect.Field;
  • java.lang.reflect.Method;
  • java.lang.reflect.Modifier;

前面我们知道了怎么获取Class,那么我们可以通过这个Class干什么呢?
总结如下:

  • 获取成员方法Method
  • 获取成员变量Field
  • 获取构造函数Constructor

下面来具体介绍:

获取成员方法信息

两个参数分别表示方法名和方法参数类的类类型列表

1
2
3
4
5
6
7
8
public Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 得到该类所有的方法,不包括父类的
public Method getMethod(String name, Class<?>... parameterTypes) // 得到该类所有的public方法,包括父类的
//具体使用
Method[] methods = class1.getDeclaredMethods();//获取class对象的所有声明方法
Method[] allMethods = class1.getMethods();//获取class对象的所有public方法 包括父类的方法
Method method = class1.getMethod("info", String.class);//返回次Class对象对应类的、带指定形参列表的public方法
Method declaredMethod = class1.getDeclaredMethod("info", String.class);//返回次Class对象对应类的、带指定形参列表的方法

举个例子:

例如类A有如下一个方法:

1
2
3
public void fun(String name,int age) {
System.out.println("我叫"+name+",今年"+age+"岁");
}

现在知道A有一个对象a,那么就可以通过:

1
2
3
4
Class c = Class.forName("com.tengj.reflect.Person"); //先生成class
Object o = c.newInstance(); //newInstance可以初始化一个实例
Method method = c.getMethod("fun", String.class, int.class);//获取方法
method.invoke(o, "tengj", 10); //通过invoke调用该方法,参数第一个为实例对象,后面为具体参数值

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class Person {
private String name;
private int age;
private String msg="hello wrold";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person() {
}
private Person(String name) {
this.name = name;
System.out.println(name);
}
public void fun() {
System.out.println("fun");
}
public void fun(String name,int age) {
System.out.println("我叫"+name+",今年"+age+"岁");
}
}
public class ReflectDemo {
public static void main(String[] args){
try {
Class c = Class.forName("com.tengj.reflect.Person");
Object o = c.newInstance();
Method method = c.getMethod("fun", String.class, int.class);
method.invoke(o, "tengj", 10);
} catch (Exception e) {
e.printStackTrace();
}
}
}

执行结果:

1
我叫tengj,今年10岁

有时候我们想获取类中所有成员方法的信息,要怎么办。可以通过以下几步来实现:

  • 获取所有方法的数组:

    1
    2
    3
    4
    Class c = Class.forName("com.tengj.reflect.Person");
    Method[] methods = c.getDeclaredMethods(); // 得到该类所有的方法,不包括父类的
    或者:
    Method[] methods = c.getMethods();// 得到该类所有的public方法,包括父类的
  • 然后循环这个数组就得到每个方法了:

    1
    for (Method method : methods)

完整代码如下:
person类跟上面一样,这里以及后面就不贴出来了,只贴关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ReflectDemo {
public static void main(String[] args){
try {
Class c = Class.forName("com.tengj.reflect.Person");
Method[] methods = c.getDeclaredMethods();
for(Method m:methods){
String methodName= m.getName();
System.out.println(methodName);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

执行结果如下:

1
2
3
4
5
6
getName
setName
setAge
fun
fun
getAge

这里如果把c.getDeclaredMethods();改成c.getMethods();执行结果如下,多了很多方法,以为把Object里面的方法也打印出来了,因为Object是所有类的父类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
getName
setName
getAge
setAge
fun
fun
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll

获取成员变量信息

想一想成员变量中都包括什么:成员变量类型+成员变量名

类的成员变量也是一个对象,它是java.lang.reflect.Field的一个对象,所以我们通过java.lang.reflect.Field里面封装的方法来获取这些信息。

单独获取某个成员变量,通过Class类的以下方法实现:

1
2
3
4
5
6
7
8
public Field getDeclaredField(String name) // 获得该类自身声明的所有变量,不包括其父类的变量
public Field getField(String name) // 获得该类自所有的public成员变量,包括其父类变量
//具体实现
Field[] allFields = class1.getDeclaredFields();//获取class对象的所有属性
Field[] publicFields = class1.getFields();//获取class对象的public属性
Field ageField = class1.getDeclaredField("age");//获取class指定属性
Field desField = class1.getField("des");//获取class指定的public属性

举个例子:

例如一个类A有如下成员变量:

1
private int msg;

如果A有一个对象a,那么就可以这样得到其成员变量:

1
2
Class c = a.getClass();
Field field = c.getDeclaredField("msg");

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ReflectDemo {
public static void main(String[] args){
try {
Class c = Class.forName("com.tengj.reflect.Person");
//获取成员变量
Field field = c.getDeclaredField("msg"); //因为msg变量是private的,所以不能用getField方法
Object o = c.newInstance();
field.setAccessible(true);//设置是否允许访问,因为该变量是private的,所以要手动设置允许访问,如果msg是public的就不需要这行了。
Object msg = field.get(o);
System.out.println(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
}

执行结果:

1
hello wrold

同样,如果想要获取所有成员变量的信息,可以通过以下几步

  • 获取所有成员变量的数组:

    1
    Field[] fields = c.getDeclaredFields();
  • 遍历变量数组,获得某个成员变量field

    1
    for (Field field : fields)

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ReflectDemo {
public static void main(String[] args){
try {
Class c = Class.forName("com.tengj.reflect.Person");
Field[] fields = c.getDeclaredFields();
for(Field field :fields){
System.out.println(field.getName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

执行结果如下:

1
2
3
name
age
msg

获取构造函数

最后再想一想构造函数中都包括什么:构造函数参数
同上,类的成构造函数也是一个对象,它是java.lang.reflect.Constructor的一个对象,所以我们通过java.lang.reflect.Constructor里面封装的方法来获取这些信息。

单独获取某个构造函数,通过Class类的以下方法实现:

1
2
3
4
5
6
7
8
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) // 获得该类所有的构造器,不包括其父类的构造器
public Constructor<T> getConstructor(Class<?>... parameterTypes) // 获得该类所以public构造器,包括父类
//具体
Constructor<?>[] allConstructors = class1.getDeclaredConstructors();//获取class对象的所有声明构造函数
Constructor<?>[] publicConstructors = class1.getConstructors();//获取class对象public构造函数
Constructor<?> constructor = class1.getDeclaredConstructor(String.class);//获取指定声明构造函数
Constructor publicConstructor = class1.getConstructor(String.class);//获取指定声明的public构造函数

举个例子:

例如类A有如下一个构造函数:

1
2
3
public A(String a, int b) {
// code body
}

那么就可以通过:

1
Constructor constructor = a.getDeclaredConstructor(String.class, int.class);

来获取这个构造函数。

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ReflectDemo {
public static void main(String[] args){
try {
Class c = Class.forName("com.tengj.reflect.Person");
//获取构造函数
Constructor constructor = c.getDeclaredConstructor(String.class);
constructor.setAccessible(true);//设置是否允许访问,因为该构造器是private的,所以要手动设置允许访问,如果构造器是public的就不需要这行了。
constructor.newInstance("tengj");
} catch (Exception e) {
e.printStackTrace();
}
}
}

执行结果:

1
tengj

注意:Class的newInstance方法,只能创建只包含无参数的构造函数的类,如果某类只有带参数的构造函数,那么就要使用另外一种方式:

1
fromClass.getDeclaredConstructor(String.class).newInstance("tengj");

获取所有的构造函数,可以通过以下步骤实现:

  • 获取该类的所有构造函数,放在一个数组中:

    1
    Constructor[] constructors = c.getDeclaredConstructors();
  • 遍历构造函数数组,获得某个构造函数constructor:

    1
    for (Constructor constructor : constructors)

完整代码:

1
2
3
4
5
6
7
8
9
10
11
public class ReflectDemo {
public static void main(String[] args){
Constructor[] constructors = c.getDeclaredConstructors();
for(Constructor constructor:constructors){
System.out.println(constructor);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

执行结果:

1
2
public com.tengj.reflect.Person()
public com.tengj.reflect.Person(java.lang.String)

其他方法

注解需要用到的:

1
2
3
4
Annotation[] annotations = (Annotation[]) class1.getAnnotations();//获取class对象的所有注解
Annotation annotation = (Annotation) class1.getAnnotation(Deprecated.class);//获取class对象指定注解
Type genericSuperclass = class1.getGenericSuperclass();//获取class对象的直接超类的
Type Type[] interfaceTypes = class1.getGenericInterfaces();//获取class对象的所有接口的type集合

获取class对象的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
boolean isPrimitive = class1.isPrimitive();//判断是否是基础类型
boolean isArray = class1.isArray();//判断是否是集合类
boolean isAnnotation = class1.isAnnotation();//判断是否是注解类
boolean isInterface = class1.isInterface();//判断是否是接口类
boolean isEnum = class1.isEnum();//判断是否是枚举类
boolean isAnonymousClass = class1.isAnonymousClass();//判断是否是匿名内部类
boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);//判断是否被某个注解类修饰
String className = class1.getName();//获取class名字 包含包名路径
Package aPackage = class1.getPackage();//获取class的包信息
String simpleName = class1.getSimpleName();//获取class类名
int modifiers = class1.getModifiers();//获取class访问权限
Class<?>[] declaredClasses = class1.getDeclaredClasses();//内部类
Class<?> declaringClass = class1.getDeclaringClass();//外部类
getSuperclass():获取某类的父类
getInterfaces():获取某类实现的接口